/*
 * Created by SharpDevelop.
 * User: icee
 * Date: 2006-2-28
 * Time: 21:47
 */

using System;
using System.Xml;
using System.Collections;
using AxShockwaveFlashObjects;//AxImp generated module.

namespace EIFlash
{
	/// <summary>
	/// EISerializationException Class.
	/// </summary>
	public class EISerializationException:Exception
	{
		public override string Message
		{
			get{
				return "Unsupported Serialization Type.";
			}
		}
	}
	/// <summary>
	/// EIDeserializationException Class.
	/// </summary>
	public class EIDeserializationException:Exception
	{
		public override string Message
		{
			get{
				return "Unsupported Deserialization Type.";
			}
		}
	}
	/// <summary>
	/// Store External Interface call information.
	/// </summary>
	public struct EICallInfo
	{
		/// <summary>
		/// Method name.
		/// </summary>
		public string Name;
		/// <summary>
		/// Arguments.
		/// </summary>
		public object[] Args;
		public EICallInfo(string name,object[] args)
		{
			this.Name=name;
			this.Args=args;
		}
	}
	/// <summary>
	/// Do External Interface data serialization/deserialization.
	/// </summary>
	public class EIDataSerializer
	{
		XmlDocument xmlDocData;
		public EIDataSerializer()
		{
		}
		void packNumber(XmlNode p,object x)
//		void packNumber(XmlNode p,double x)
		{
			p.AppendChild(xmlDocData.CreateElement("number")
				).AppendChild(xmlDocData.CreateTextNode(x.ToString()));
		}
		void packString(XmlNode p,string x)
		{
			p.AppendChild(xmlDocData.CreateElement("string")
				).AppendChild(xmlDocData.CreateTextNode(x));
		}
		void packNull(XmlNode p)
		{
			p.AppendChild(xmlDocData.CreateElement("null"));
		}
		void packBool(XmlNode p,bool x)
		{
			if(x)
				p.AppendChild(xmlDocData.CreateElement("true"));
			else
				p.AppendChild(xmlDocData.CreateElement("false"));
		}
		void packDictionary(XmlNode p,IDictionary x)
		{
			p=p.AppendChild(xmlDocData.CreateElement("object"));
			foreach(object k in x.Keys){
				XmlNode n=p.AppendChild(xmlDocData.CreateElement("property"));
				n.Attributes.Append(xmlDocData.CreateAttribute("id")).Value=k.ToString();
				packData(n,x[k]);
			}
		}
		void packList(XmlNode p,IList x)
		{
			p=p.AppendChild(xmlDocData.CreateElement("array"));
			for(int i=0;i<x.Count;i++){
			XmlNode n=p.AppendChild(xmlDocData.CreateElement("property"));
				n.Attributes.Append(xmlDocData.CreateAttribute("id")).Value=i.ToString();
				packData(n,x[i]);
			}
		}
		private static Type[] _TypeNumbers=new Type[]{typeof(Int16),
			typeof(Int32),
			typeof(Int64),
			typeof(UInt16),
			typeof(UInt32),
			typeof(UInt64),
			typeof(Double),
			typeof(Decimal)
		};
		void packData(XmlNode p,object x)
		{
			if(x==null){
				packNull(p);
				return;
			}
			Type t=x.GetType();
			if(((IList)_TypeNumbers).IndexOf(t)>-1){
				packNumber(p,x);
				return;
			}
			if(t==typeof(string)){
				packString(p,(string)x);
				return;
			}
			if(t==typeof(bool)){
				packBool(p,(bool)x);
				return;
			}
			if(x is IList){
				packList(p,(IList)x);
				return;
			}
			if(x is IDictionary){
				packDictionary(p,(IDictionary)x);
				return;
			}
			throw new EISerializationException();
		}
		object unpackNumber(XmlNode p)
//		double unpackNumber(XmlNode p)
		{
			try{
				return Convert.ToInt32(p.FirstChild.Value);
			}
			catch(FormatException){
				try{
					return Convert.ToDouble(p.FirstChild.Value);
				}
				catch(FormatException){
					return Double.NaN;
				}
			}
		}
		string unpackString(XmlNode p)
		{
			return p.FirstChild.Value;
		}
		bool unpackTrue()
		{
			return true;
		}
		bool unpackFalse()
		{
			return false;
		}
		object unpackNull()
		{
			return null;
		}
		object unpackUndefined()
		{
			return null;
		}
		Hashtable unpackObject(XmlNode p)
		{
			Hashtable d=new Hashtable();
			foreach(XmlNode n in p.ChildNodes){
				d[n.Attributes["id"].Value]=unpackData(n.FirstChild);
			}
			return d;
		}
		ArrayList unpackArray(XmlNode p)
		{
			ArrayList a=new ArrayList();
			foreach(XmlNode n in p.ChildNodes){
				a.Add(unpackData(n.FirstChild));
			}
			return a;
		}
		object unpackData(XmlNode p)
		{
			string t=p.Name;
			switch(t){
				case "number":
					return unpackNumber(p);
				case "string":
					return unpackString(p);
				case "true":
					return unpackTrue();
				case "false":
					return unpackFalse();
				case "null":
					return unpackNull();
				case "undefined":
					return unpackUndefined();
				case "object":
					return unpackObject(p);
				case "array":
					return unpackArray(p);
				default:
//					return null;
					throw new EIDeserializationException();
			}
		}
		/// <summary>
		/// Serialize return data.
		/// </summary>
		/// <param name="v">Return data.</param>
		/// <returns>Serialized return.</returns>
		public string SerializeReturn(object v)
		{
			xmlDocData=new XmlDocument();
			packData(xmlDocData,v);
			return xmlDocData.DocumentElement.OuterXml;
		}
		/// <summary>
		/// Serialize call data.
		/// </summary>
		/// <param name="name">Method name.</param>
		/// <param name="args">Arguments.</param>
		/// <returns>Serialized call.</returns>
		public string SerializeCall(string name,object[] args)
		{
			xmlDocData=new XmlDocument();
			XmlNode p=xmlDocData.AppendChild(xmlDocData.CreateElement("invoke"));
			XmlAttribute a;
			a=p.Attributes.Append(xmlDocData.CreateAttribute("name"));
			a.Value=name;
			a=p.Attributes.Append(xmlDocData.CreateAttribute("returntype"));
			a.Value="xml";
			p=p.AppendChild(xmlDocData.CreateElement("arguments"));
			foreach(object v in args){
				packData(p,v);
			}
			return xmlDocData.DocumentElement.OuterXml;
		}
		/// <summary>
		/// Deserialize return data.
		/// </summary>
		/// <param name="s">Return data.</param>
		/// <returns>Deserialized return.</returns>
		public object DeserializeReturn(string s)
		{
			xmlDocData=new XmlDocument();
			xmlDocData.LoadXml(s);
			return unpackData(xmlDocData.DocumentElement);
		}
		/// <summary>
		/// Deserialize call data.
		/// </summary>
		/// <param name="s">Call data.</param>
		/// <returns>Deserialized call.</returns>
		public EICallInfo DeserializeCall(string s)
		{
			xmlDocData=new XmlDocument();
			xmlDocData.LoadXml(s);
			XmlNode p=xmlDocData.DocumentElement;//invoke
			string name=p.Attributes["name"].Value;
			p=p.FirstChild;//arguments
			object[] args=new object[p.ChildNodes.Count];
			for(int i=0;i<p.ChildNodes.Count;i++){
				args[i]=unpackData(p.ChildNodes[i]);
			}
			return new EICallInfo(name,args);
		}
	}
	/// <summary>
	/// Extended AxShockwaveFlash Control.
	/// </summary>
	public class AxShockwaveFlashEx:AxShockwaveFlash
	{
		EIDataSerializer szr;
		Hashtable callbackReg;

		public AxShockwaveFlashEx():base()
		{
			szr=new EIDataSerializer();
			callbackReg=new Hashtable();
			FlashCall+=new _IShockwaveFlashEvents_FlashCallEventHandler(_OnFlashCall);

		}
		/// <summary>
		/// Invoke Flash-side methods.
		/// </summary>
		/// <param name="name">Flash-side method name.</param>
		/// <param name="args">Arguments to be sent.</param>
		/// <returns>Flash-side return value.</returns>
		public object CallFlash(string name,object[] args)
		{
			string s=CallFunction(szr.SerializeCall(name,args));
			return szr.DeserializeReturn(s);
		}
		public object CallFlashEx(string name,params object[] args)
		{
			string s=CallFunction(szr.SerializeCall(name,args));
			return szr.DeserializeReturn(s);
		}
		
		/// <summary>
		/// Register a callback method that can be invoked by Flash-side script.
		/// </summary>
		/// <param name="name">Method name that can be called by Flash-side.</param>
		/// <param name="method">Local callback delegation.</param>
		public void RegisterCallback(string name,Delegate method)
		{
			callbackReg.Add(name,method);
		}
		
		void _OnFlashCall(object sender,AxShockwaveFlashObjects._IShockwaveFlashEvents_FlashCallEvent e)
		{
			EICallInfo callinfo=szr.DeserializeCall(e.request);
			if(callbackReg.Contains(callinfo.Name)){
				object v=((Delegate)callbackReg[callinfo.Name]).DynamicInvoke(callinfo.Args);
				SetReturnValue(szr.SerializeReturn(v));
			}
		}
	}
}
